﻿/* Copyright (c) Microsoft Corporation
 * 
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * 
 * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
 * 
 * See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.ComponentModel;
using System.Threading;
using System.Text.RegularExpressions;

namespace ProximityTapper
{
    class ProximityDeviceConnectInfo : INotifyPropertyChanged
    {
        static Regex regexIpAddress = new Regex(@"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$");
        static Regex regexMacAddress = new Regex(@"^([0-9a-fA-F]{2}[:\- ]?){5}[0-9a-fA-F]{2}$");
         
        public ProximityDeviceConnectInfo(byte[] driverBroadcast, IPEndPoint ep)
        {
            pingTimeoutTimer = new Timer(PingTimeoutTimer_Elapsed, this, Timeout.Infinite, Timeout.Infinite);
            UpdateDriverBroadcast(driverBroadcast, ep);
        }

        public ProximityDeviceConnectInfo(string info)
        {
            pingTimeoutTimer = new Timer(PingTimeoutTimer_Elapsed, this, Timeout.Infinite, Timeout.Infinite);

            var data = info.Split(new char[] {'~'}, 2);

            if(regexIpAddress.IsMatch(data[0]))
            {
                IpAddress = data[0];
            }
            else if (regexMacAddress.IsMatch(data[0]))
            {
                MacAddress = data[0].Replace("-", "").Replace(":", "").Replace(" ", "");
            }
            else
            {
                IpAddress = data[0];
            }

            if (data.Length > 1)
            {
                Name = data[1];
            }
        }

        public override string ToString()
        {
            return (string.IsNullOrWhiteSpace(MacAddress) ? IpAddress : MacAddress) + (string.IsNullOrWhiteSpace(Name) ? "" : "~" + Name);
        }

        private Timer pingTimeoutTimer = null;
        private static void PingTimeoutTimer_Elapsed(object state)
        {
            if (state != null)
            {
                var p = (ProximityDeviceConnectInfo)state;

                if (p.LastPing == null || (DateTime.Now - (DateTime)p.LastPing).TotalSeconds > 22)
                {
                    p.IsOnline = false;
                }
            }
        }

        private DateTime? _lastPing;
        public DateTime? LastPing
        {
            get { return _lastPing; }
            set { if (_lastPing != value) { _lastPing = value; IsOnline = true; pingTimeoutTimer.Change(25000, Timeout.Infinite); OnPropertyChanged("LastPing"); } }
        }

        private bool _isOnline = false;
        public bool IsOnline
        {
            get { return _isOnline; }
            set { if (_isOnline != value) { _isOnline = value; OnPropertyChanged("IsOnline"); OnPropertyChanged("Caption"); } }
        }
        
        public void UpdateDriverBroadcast(byte[] driverBroadcast, IPEndPoint ep)
        {
            this.IpAddress = ep.Address.ToString();
            if (driverBroadcast != null)
            {
                string[] broadcast = System.Text.Encoding.Unicode.GetString(driverBroadcast).Split('\x01');
                if (broadcast.Length >= 3)
                {
                    MacAddress = broadcast[2];
                    if (string.IsNullOrWhiteSpace(this.Name))
                    {
                        this.Name = broadcast[0];
                    }
                    this.FriendlyName = broadcast[0];
                    this.BuildString = broadcast[1];
                }
            }
            LastPing = DateTime.Now;
        }

        public void Update(ProximityDeviceConnectInfo c)
        {
            if (!string.IsNullOrWhiteSpace(c.IpAddress))
            {
                this.IpAddress = c.IpAddress;
            }

            if (!string.IsNullOrWhiteSpace(c.Name) && string.IsNullOrWhiteSpace(this.Name))
            {
                this.Name = c.Name;
            }

            if (!string.IsNullOrWhiteSpace(c.MacAddress) && string.IsNullOrWhiteSpace(this.MacAddress))
            {
                this.MacAddress = c.MacAddress;
            }

            this.BuildString = c.BuildString;
            this.FriendlyName = c.FriendlyName;
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { if (_name != value) { _name = value; OnPropertyChanged("Name"); OnPropertyChanged("Caption"); } }
        }

        private string _ipAddress;
        public string IpAddress
        {
            get { return _ipAddress; }
            set { if (_ipAddress != value) { _ipAddress = value; OnPropertyChanged("IpAddress"); OnPropertyChanged("Caption"); } }
        }        

        private string _macAddress;
        public string MacAddress
        {
            get { return _macAddress; }
            set { if (_macAddress != value) { _macAddress = value; OnPropertyChanged("MacAddress"); OnPropertyChanged("Caption"); } }
        }

        private string _buildString;
        public string BuildString
        {
            get { return _buildString; }
            set { if (_buildString != value) { _buildString = value; OnPropertyChanged("BuildString"); } }
        }

        private string _friendlyName;
        public string FriendlyName
        {
            get { return _friendlyName; }
            set { if (_friendlyName != value) { _friendlyName = value; OnPropertyChanged("FriendlyName"); } }
        }

        public bool Matches(ProximityDeviceConnectInfo p)
        {
            return p != null 
                && (p.MacAddress != null && p.MacAddress.Equals(this.MacAddress, StringComparison.InvariantCultureIgnoreCase))
                 || ((string.IsNullOrWhiteSpace(p.MacAddress) || string.IsNullOrWhiteSpace(this.MacAddress)) && p.IpAddress == this.IpAddress);
        }

        public string Caption
        {
            get
            {
                return (Name ?? "") + " - " + (IpAddress ?? "") + " - " + (MacAddress ?? "") + (IsOnline == true ? " ONLINE" : "");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                System.Windows.Application.Current.Dispatcher.BeginInvoke(new ThreadStart(() =>
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }));
            }
        }
    }
}
